package com.sunther.document.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunther.document.Enum.DictEnum;
import com.sunther.document.Enum.ExportHeaderEnum;
import com.sunther.document.dao.FileDao;
import com.sunther.document.entity.*;
import com.sunther.document.service.*;
import com.sunther.document.util.ExportExcelUtil;
import com.sunther.document.util.FileUtil;
import com.sunther.document.util.UserInfoUtil;
import com.sunther.idb.common.util.WebResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import static com.sunther.document.util.FileUtil.supportedTypes;

/**
 * @author csb
 * @description: TODO
 * @date 2023/5/24 10:12
 */
@Service
public class FileServiceImpl extends ServiceImpl<FileDao, FileEntity> implements FileService {

    @Value("${file.remoteRootPath}")
    String remoteRootPath;

    @Autowired
    private FileMidService fileMidService;

    @Autowired
    private FileTagService fileTagService;

    @Autowired
    private FileDao fileDao;

    @Autowired
    private DictService dictService;

    @Autowired
    private UserService userService;

    @Autowired
    private FileSystemService fileSystemService;

    @Autowired
    private MsgService msgService;

    @Override
    public WebResult upload(MultipartFile[] files) throws Exception {
        if (files == null || files.length == 0) {
            return WebResult.failure("文件不能为空");
        }
        ArrayList<FileMidEntity> list = new ArrayList<>();
        for (MultipartFile file : files) {
            //文件类型过滤
            String ext = FileUtil.getFileExt(file.getOriginalFilename());
            if (!supportedTypes(ext)){
                return WebResult.failure("不支持的文件格式");
            }

            String localFileName = file.getOriginalFilename();
            String remoteFileName = System.currentTimeMillis()+"-"+localFileName;

            String date = DateTime.now().toString("yyyyMMdd");
            String relativeFilePath = "/"+date+"/"+remoteFileName;
            File dire = new File(remoteRootPath + "/"+date);
            if(!dire.exists()){
                dire.mkdir();
            }
            File remoteFile = new File(remoteRootPath+relativeFilePath);
            InputStream input = FileUtil.fileToStream(file);
            FileUtil.streamToFile(input, remoteFile);

            FileMidEntity fileEntity = new FileMidEntity();
            fileEntity.setId(IdUtil.simpleUUID());
            fileEntity.setName(localFileName);
            fileEntity.setUrl(relativeFilePath);

            list.add(fileEntity);
        }

        fileMidService.saveBatch(list);
        return WebResult.success("保存成功",list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebResult saveFileData(FileEntity data, HttpServletRequest request) {
        Integer userId = UserInfoUtil.getUserId(request);
        Integer roleId = UserInfoUtil.getRoleId(request);
        String nickName = UserInfoUtil.getNickName(request);
        if (data.getId() == null) {
            data.init(String.valueOf(userId));
            String uuid = IdUtil.simpleUUID();
            data.setId(uuid);
            if (StrUtil.isBlank(data.getFileTraId()) && StrUtil.isNotBlank(data.getFileOrgId())){
                data.setUploaderOrg(userId);
                data.setFileState(DictEnum.FILE_STATE_TO_BE_TRA.getCode());
                //发送消息
                msgService.saveMag(DictEnum.MSG_CONTENT_ORG.getCode(),userId,nickName,data.getProjectId());
            }
        }else {
            data.modify(String.valueOf(userId));
            //如果是项目负责人和项目参与人 翻译中和翻译完成 不能编辑
            if (DictEnum.ROLE_PROJECT_LEADER.getCode().equals(roleId) || DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode().equals(roleId)){
                if (data.getFileState().equals(DictEnum.FILE_STATE_TRANSLATING.getCode()) || data.getFileState().equals(DictEnum.FILE_STATE_COMPLETE.getCode())){
                    return WebResult.failure("禁止编辑");
                }
            }
            //项目参与人只能编辑自己的文件
            if (DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode().equals(roleId)){
                if (!Objects.equals(userId,data.getUploaderOrg())){
                    return WebResult.failure("暂无编辑该文件权限");
                }
            }
            //翻译中 管理员或者译员 且上传了译文
            if (DictEnum.ROLE_ADMIN.getCode().equals(roleId) || DictEnum.ROLE_interpreter.getCode().equals(roleId)){
                if (StrUtil.isNotBlank(data.getFileTraId()) && DictEnum.FILE_STATE_TRANSLATING.getCode().equals(data.getFileState())){
                    data.setUploaderTra(userId);
                    data.setUploadTimeTra(DateTime.now());
                    data.setFileState(DictEnum.FILE_STATE_COMPLETE.getCode());
                    //发送消息
                    msgService.saveMag(DictEnum.MSG_CONTENT_TRAN.getCode(),userId,nickName,data.getProjectId());
                }
            }

            //先删除 tag数据 再保存
            QueryWrapper<FileTagEntity> wrapper = new QueryWrapper<>();
            wrapper.eq("file_id",data.getId());
            fileTagService.remove(wrapper);
        }

        if (null != data.getTagName() && data.getTagName().size() > 0){
            ArrayList<FileTagEntity> list = new ArrayList<>();
            List<String> tagName = data.getTagName();
            tagName.stream().forEach(a -> {
                FileTagEntity entity = new FileTagEntity();
                entity.setName(a);
                entity.setFileId(data.getId());
                entity.setProjectId(data.getProjectId());
                list.add(entity);
            });
            fileTagService.saveBatch(list);
        }

        saveOrUpdate(data);
        return WebResult.success("保存成功",data);
    }

    @Override
    public void downloadFile(String id, String fileId,Integer roleId, HttpServletRequest request, HttpServletResponse response) throws IOException {

        FileEntity fileEntity = getById(id);

        if (DictEnum.ROLE_ADMIN.getCode().equals(roleId) || DictEnum.ROLE_interpreter.getCode().equals(roleId)){
            //管理员下载待翻译的文件 - 状态变为翻译中
            if (DictEnum.FILE_STATE_TO_BE_TRA.getCode().equals(fileEntity.getFileState())){
                fileEntity.setFileState(DictEnum.FILE_STATE_TRANSLATING.getCode());
            }
        }
        Integer userId = UserInfoUtil.getUserId(request);
        fileEntity.modify(String.valueOf(userId));

        updateById(fileEntity);

        FileMidEntity midEntity = fileMidService.getById(fileId);

        FileUtil.downloadFile(remoteRootPath+midEntity.getUrl(),request,response);
    }

    @Override
    public Page pageList(HttpServletRequest request, String keyword, String tagName, Integer state, String systemId, Integer uploadUserId,String selectProId,int pageNumber, int pageSize) {

        String projectId = UserInfoUtil.getProjectId(request);
        Integer roleId = UserInfoUtil.getRoleId(request);
        Integer userId = UserInfoUtil.getUserId(request);

        Page pageInfo = new Page(pageNumber,pageSize);
        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(keyword)){
            wrapper.and(wrap -> wrap.like("name",keyword).or().like("mark",keyword));
        }
        if (StrUtil.isNotBlank(systemId)){
            wrapper.eq("system_id",systemId);
        }
        if (null != state){
            wrapper.eq("file_state",state);
        }
        if (StrUtil.isNotBlank(tagName)){
            QueryWrapper<FileTagEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name",tagName);
            List<FileTagEntity> tagEntities = fileTagService.list(queryWrapper);
            List<String> ids = tagEntities.stream().map(FileTagEntity::getFileId).collect(Collectors.toList());
            wrapper.in("id",ids);
        }
        // 项目参与人只能看到项目 自己的文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode())){
            wrapper.eq("project_id",projectId);
            wrapper.eq("uploader_org",userId);
        }
        // 项目负责人  能看到项目所有文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_LEADER.getCode()) ){
            wrapper.eq("project_id",projectId);
        }
        // 管理员 译员 可选择项目
        if (roleId.equals(DictEnum.ROLE_ADMIN.getCode()) || roleId.equals(DictEnum.ROLE_interpreter.getCode()) ){
            wrapper.eq("project_id",selectProId);
        }
        if (null != uploadUserId){
            wrapper.eq("uploader_org",uploadUserId);
        }
        wrapper.orderByDesc("update_time");
        Page page = fileDao.selectPage(pageInfo, wrapper);

        getText(page.getRecords());
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebResult cancel(String id) {
        //删除文件
        FileMidEntity fileMidEntity = fileMidService.getById(id);
        String url = remoteRootPath + fileMidEntity.getUrl();
        FileUtil.deleteFile(url);
        fileMidService.removeById(id);
        return WebResult.success("操作成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebResult delete(String id,HttpServletRequest request) {

        Integer roleId = UserInfoUtil.getRoleId(request);
        Integer userId = UserInfoUtil.getUserId(request);

        FileEntity fileEntity = getById(id);

        //如果是项目负责人和项目参与人 翻译中和翻译完成 不能删除
        if (DictEnum.ROLE_PROJECT_LEADER.getCode().equals(roleId) || DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode().equals(roleId)){
            if (fileEntity.getFileState().equals(DictEnum.FILE_STATE_TRANSLATING.getCode()) || fileEntity.getFileState().equals(DictEnum.FILE_STATE_COMPLETE.getCode())){
                return WebResult.failure("文件状态处于“翻译中”和“翻译完成”时，禁止删除");
            }
        }

        //项目参与人只能删除自己的文件
        if (DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode().equals(roleId)){
            if (!Objects.equals(userId,fileEntity.getUploaderOrg())){
                return WebResult.failure("暂无删除该文件权限");
            }
        }

        //删除原文文件
        FileMidEntity fileMidEntity = fileMidService.getById(fileEntity.getFileOrgId());
        String url = remoteRootPath + fileMidEntity.getUrl();
        FileUtil.deleteFile(url);
        fileMidService.removeById(fileMidEntity.getId());

        //删除译文文件
        if (StrUtil.isNotBlank(fileEntity.getFileTraId())){
            FileMidEntity fileMidEntity2 = fileMidService.getById(fileEntity.getFileTraId());
            String url2 = remoteRootPath + fileMidEntity2.getUrl();
            FileUtil.deleteFile(url2);
            fileMidService.removeById(fileMidEntity2.getId());
        }

        //删除文件标签
        QueryWrapper<FileTagEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("file_id",id);
        fileTagService.remove(wrapper);

        removeById(id);
        return WebResult.success("操作成功");
    }

    @Override
    public WebResult getUploaderByProject(String projectId) {
        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("project_id",projectId);
        List<FileEntity> list = list(wrapper);
        Set<Integer> collect = list.stream().map(FileEntity::getUploaderOrg).collect(Collectors.toSet());

        List<User> user = userService.getUserByIds(collect);
        return WebResult.success("获取成功",user);
    }

    @Override
    public WebResult getStateAndCount(String selectProId, String systemId, String keyword, String tagName, Integer uploadUserId, HttpServletRequest request) {

        String projectId = UserInfoUtil.getProjectId(request);
        Integer roleId = UserInfoUtil.getRoleId(request);
        Integer userId = UserInfoUtil.getUserId(request);

        List<DictEntity> dict = (List<DictEntity>) dictService.findByType("trans_state").getEntity();

        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();

        if (StrUtil.isNotEmpty(keyword)){
            wrapper.and(wrap -> wrap.like("name",keyword).or().like("mark",keyword));
        }
        if (StrUtil.isNotBlank(systemId)){
            wrapper.eq("system_id",systemId);
        }
        if (StrUtil.isNotBlank(tagName)){
            QueryWrapper<FileTagEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name",tagName);
            List<FileTagEntity> tagEntities = fileTagService.list(queryWrapper);
            List<String> ids = tagEntities.stream().map(FileTagEntity::getFileId).collect(Collectors.toList());
            wrapper.in("id",ids);
        }
        // 项目参与人只能看到项目 自己的文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode())){
            wrapper.eq("project_id",projectId);
            wrapper.eq("uploader_org",userId);
        }
        // 项目负责人  能看到项目所有文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_LEADER.getCode()) ){
            wrapper.eq("project_id",projectId);
        }
        // 管理员 译员 可选择项目
        if (roleId.equals(DictEnum.ROLE_ADMIN.getCode()) || roleId.equals(DictEnum.ROLE_interpreter.getCode()) ){
            wrapper.eq("project_id",selectProId);
        }
        if (null != uploadUserId){
            wrapper.eq("uploader_org",uploadUserId);
        }
        List<FileEntity> fileEntityList = list(wrapper);
        Map<Integer, List<FileEntity>> map = fileEntityList.stream().collect(Collectors.groupingBy(FileEntity::getFileState));

        dict.stream().forEach(a -> {
            map.forEach((k,v) -> {
                if (Objects.equals(a.getDictCode(),k)){
                    a.setCount(v.stream().count());
                }
            });

        });

        return WebResult.success("获取成功",dict);
    }

    @Override
    public WebResult dtl(String id) {
        FileEntity entity = getById(id);
        ArrayList<FileEntity> list = new ArrayList<>();
        list.add(entity);
        getText(list);
        FileEntity fileEntity = list.get(0);
        return WebResult.success(fileEntity);
    }

    @Override
    public long getCountByProjectId(String projectId, Integer roleId, Integer userId) {

        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();

        // 项目参与人只能看到项目 自己的文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode())){
            wrapper.eq("uploader_org",userId);
        }

        wrapper.eq("project_id",projectId);
        long count = count(wrapper);
        return count;
    }

    @Override
    public void export(String keyword, String tagName, Integer state, String systemId, Integer uploadUserId, String selectProId, HttpServletRequest request, HttpServletResponse response) throws Exception {

        List<FileEntity> list = listByKeyWord(keyword,tagName,state,systemId,uploadUserId,selectProId,request);
        String title = "文件";
        String[] rowName = ExportHeaderEnum.FILE_ROW_NAME;
        List<Object[]> dataList = new ArrayList<>();

        int i = 0;
        for (FileEntity fileEntity : list) {
            Object[] obj = new Object[rowName.length];
            obj[0] = i++;
            obj[1] = fileEntity.getName();
            obj[2] = fileEntity.getSystemName();
            obj[3] = fileEntity.getMark();
            obj[4] = fileEntity.getTagName();
            obj[5] = fileEntity.getFileStateText();
            obj[6] = fileEntity.getTransToText();
            obj[7] = fileEntity.getWordCount();
            obj[8] = fileEntity.getUploaderOrgText();
            obj[9] = DateUtil.format(fileEntity.getCreateTime(),"yyyy-MM-dd HH:mm:ss");
            obj[10] = fileEntity.getUploaderTraText();
            obj[11] = DateUtil.format(fileEntity.getUploadTimeTra(),"yyyy-MM-dd HH:mm:ss");
            dataList.add(obj);
        }

        ExportExcelUtil exportExcelUtil = new ExportExcelUtil(title, rowName, dataList, response);
        exportExcelUtil.export();
    }

    private List<FileEntity> listByKeyWord(String keyword, String tagName, Integer state, String systemId, Integer uploadUserId, String selectProId, HttpServletRequest request) {
        String projectId = UserInfoUtil.getProjectId(request);
        Integer roleId = UserInfoUtil.getRoleId(request);
        Integer userId = UserInfoUtil.getUserId(request);

        QueryWrapper<FileEntity> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotEmpty(keyword)){
            wrapper.and(wrap -> wrap.like("name",keyword).or().like("mark",keyword));
        }
        if (StrUtil.isNotBlank(systemId)){
            wrapper.eq("system_id",systemId);
        }
        if (null != state){
            wrapper.eq("file_state",state);
        }
        if (StrUtil.isNotBlank(tagName)){
            QueryWrapper<FileTagEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name",tagName);
            List<FileTagEntity> tagEntities = fileTagService.list(queryWrapper);
            List<String> ids = tagEntities.stream().map(FileTagEntity::getFileId).collect(Collectors.toList());
            wrapper.in("id",ids);
        }
        // 项目参与人只能看到项目 自己的文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_PARTICIPANTS.getCode())){
            wrapper.eq("project_id",projectId);
            wrapper.eq("uploader_org",userId);
        }
        // 项目负责人  能看到项目所有文件
        if (roleId.equals(DictEnum.ROLE_PROJECT_LEADER.getCode()) ){
            wrapper.eq("project_id",projectId);
        }
        // 管理员 译员 可选择项目
        if (roleId.equals(DictEnum.ROLE_ADMIN.getCode()) || roleId.equals(DictEnum.ROLE_interpreter.getCode()) ){
            wrapper.eq("project_id",selectProId);
        }
        if (null != uploadUserId){
            wrapper.eq("uploader_org",uploadUserId);
        }
        wrapper.orderByDesc("update_time");

        List<FileEntity> list = list(wrapper);
        getText(list);
        return list;
    }

    private void getText(List<FileEntity> records) {
        if (ArrayUtil.isNotEmpty(records) && records.size() > 0) {
            Map<String, List<String>> tagNameByMap = fileTagService.getTagNameByMap();
            Map<Integer, String> transState = dictService.getMapByType("trans_state");
            Map<Integer, String> transTo = dictService.getMapByType("trans_to");
            Map<String, String> systemNameMap = fileSystemService.getSystemNameMap();

            List<String> orgIds = records.stream().filter(a -> StrUtil.isNotBlank(a.getFileOrgId())).map(FileEntity::getFileOrgId).collect(Collectors.toList());
            List<String> traIds = records.stream().filter(a -> StrUtil.isNotBlank(a.getFileTraId())).map(FileEntity::getFileTraId).collect(Collectors.toList());
            Map<String, String> orgNameMap = fileMidService.getNameMapByIds(orgIds);
            Map<String, String> traNameMap = fileMidService.getNameMapByIds(traIds);

            Set<Integer> ids = new HashSet<>();
            records.stream().forEach(a -> {
                ids.add(a.getUploaderOrg());
                ids.add(a.getUploaderTra());
            });
            Map<Long, String> nickNameMapByIds = userService.getUserNickNameMapByIds(ids);
            records.stream().forEach(a -> {
                a.setSystemName(systemNameMap.get(a.getSystemId()));
                a.setTagName(tagNameByMap.get(a.getId()));
                a.setFileStateText(transState.get(a.getFileState()));
                a.setTransToText(transTo.get(a.getTransTo()));
                a.setUploaderOrgText(nickNameMapByIds.get(a.getUploaderOrg().longValue()));
                if (null != a.getUploaderTra()) {
                    a.setUploaderTraText(nickNameMapByIds.get(a.getUploaderTra().longValue()));
                }
                a.setFileOrgName(orgNameMap.get(a.getFileOrgId()));
                if (null != a.getFileTraId()){
                    a.setFileTraName(traNameMap.get(a.getFileTraId()));
                }

            });
        }
    }
}
